﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Storage;

namespace BenevolentSunData
{
    // Wishlist
    //texturing
    //specular texturing
    //multitexturing
    public class Material
    {

        #region Material Properties
        Color diffuseColor;
        [ContentSerializer(Optional = true)]
        public Color DiffuseColor
        {
            get { return diffuseColor; }
            set { 
                diffuseColor = value;
                if (effect != null)
                structDiffuseColor.SetValue(diffuseColor.ToVector3());
            }
        }

        Color emissiveColor;
        [ContentSerializer(Optional = true)]
        public Color EmissiveColor 
        {
            get { return emissiveColor; }
            set { 
                emissiveColor = value;
                if (effect != null)
                structEmissiveColor.SetValue(emissiveColor.ToVector3());
            }
        }

        Color specularColor;
        [ContentSerializer(Optional = true)]
        public Color SpecularColor
        {
            get { return specularColor; }
            set { 
                specularColor = value;
                if (effect != null)
                structSpecularColor.SetValue(specularColor.ToVector3());
            }
        }

        float specularPower;
        [ContentSerializer(Optional = true)]
        public float SpecularPower
        {
            get { return specularPower; }
            set {
                specularPower = value;
                if (effect != null)
                    structSpecularPower.SetValue(specularPower);
            }
        }

        float alpha;
        [ContentSerializer(Optional = true)]
        public float Alpha
        {
            get { return alpha; }
            set {
                alpha = value;
                if (effect != null)
                structAlpha.SetValue(alpha);
            }
        }
        #endregion


        #region Effect / Graphics Handles
        GraphicsDevice graphicsDevice;

        [ContentSerializerIgnore]
        public bool ShaderModel3;

        Effect baseEffect;

        Effect effect;
        [ContentSerializerIgnore]
        public Effect Effect
        {
            get { return effect; }
        }

        EffectParameter structInstance, structDiffuseColor, structAlpha, structEmissiveColor, structSpecularColor, structSpecularPower;
        #endregion


        #region Initialization
        public Material(GraphicsDevice graphicsDevice, Effect baseEffect)
        {
            effect = baseEffect.Clone(graphicsDevice);
            this.baseEffect = baseEffect;

            structInstance = effect.Parameters["Material"];
            structDiffuseColor = structInstance.StructureMembers["DiffuseColor"];
            structAlpha = structInstance.StructureMembers["Alpha"];
            structEmissiveColor = structInstance.StructureMembers["EmissiveColor"];
            structSpecularColor = structInstance.StructureMembers["SpecularColor"];
            structSpecularPower = structInstance.StructureMembers["SpecularPower"];

            DiffuseColor = Color.White;
            EmissiveColor = Color.Black;
            SpecularColor = Color.White;
            SpecularPower = 16f;
            Alpha = 1f;
        }

        public Material()
        {
            DiffuseColor = Color.White;
            EmissiveColor = Color.Black;
            SpecularColor = Color.White;
            SpecularPower = 16f;
            Alpha = 1f;
        }

        public void LoadContent(ContentManager content, GraphicsDevice graphicsDevice)
        {
            //Console.WriteLine("loaded content");
            if ((graphicsDevice.GraphicsDeviceCapabilities.PixelShaderVersion.Major >= 3) && !Settings.ForceShaderModel_2_0)
            {
                baseEffect = content.Load<Effect>("Effects/SwitchEffect_3_0");
                ShaderModel3 = true;
            }
            else
            {
                baseEffect = content.Load<Effect>("Effects/SwitchEffect_2_0");
                ShaderModel3 = false;
            }

            this.graphicsDevice = graphicsDevice;
            effect = baseEffect.Clone(baseEffect.GraphicsDevice);

            structInstance = effect.Parameters["Material"];
            structDiffuseColor = structInstance.StructureMembers["DiffuseColor"];
            structAlpha = structInstance.StructureMembers["Alpha"];
            structEmissiveColor = structInstance.StructureMembers["EmissiveColor"];
            structSpecularColor = structInstance.StructureMembers["SpecularColor"];
            structSpecularPower = structInstance.StructureMembers["SpecularPower"];

            DiffuseColor = diffuseColor;
            EmissiveColor = emissiveColor;
            SpecularColor = specularColor;
            SpecularPower = specularPower;
            Alpha = alpha;
        }
        #endregion




        

        public Material Clone()
        {
            Material skin = new Material(baseEffect.GraphicsDevice, baseEffect);

            skin.DiffuseColor = new Color(DiffuseColor.ToVector3());
            skin.SpecularColor = new Color(SpecularColor.ToVector3());
            skin.SpecularPower = SpecularPower;
            skin.EmissiveColor = EmissiveColor;
            skin.Alpha = Alpha;

            skin.graphicsDevice = graphicsDevice;

            skin.ShaderModel3 = ShaderModel3;

            return skin;
        }




        public void DrawMesh(ModelMesh mesh, Matrix world)
        {
            foreach (ModelMeshPart meshPart in mesh.MeshParts)
            {
                //set the vertex source to the mesh's vertex buffer
                graphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride);

                //set the vertex delclaration
                graphicsDevice.VertexDeclaration = meshPart.VertexDeclaration;

                //set the current index buffer to the sample mesh's index buffer
                graphicsDevice.Indices = mesh.IndexBuffer;

                // use the current effect
                
                effect.Parameters["World"].SetValue(world);


                //at this point' we're ready to begin drawing
                //To start using any effect, you must call Effect.Begin
                //to start using the current technique (set in LoadGraphicsContent)
                effect.Begin(SaveStateMode.None);

                effect.CommitChanges(); //?

                //EffectPass.Begin will update the device to begin using the state information defined in the current pass
                effect.CurrentTechnique.Passes["Shadow"].Begin();
                //Console.WriteLine(effect.CurrentTechnique.Passes["Ambient"].Name);

                //sampleMesh contains all of the information required to draw the current mesh
                graphicsDevice.DrawIndexedPrimitives(
                    PrimitiveType.TriangleList, meshPart.BaseVertex, 0,
                    meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);

                //EffectPass.End must be called when the effect is no longer needed
                effect.CurrentTechnique.Passes["Shadow"].End();


                //Likewise, Effect.End will end the current technique
                effect.End();
            }
        }


        public void DrawMesh(ModelMesh mesh, Matrix world, List<PointLight> pointLights, List<BeamLight> beamLights)
        {
            foreach (ModelMeshPart meshPart in mesh.MeshParts)
            {
                //set the vertex source to the mesh's vertex buffer
                graphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride);

                //set the vertex delclaration
                graphicsDevice.VertexDeclaration = meshPart.VertexDeclaration;

                //set the current index buffer to the sample mesh's index buffer
                graphicsDevice.Indices = mesh.IndexBuffer;

                
                effect.Parameters["World"].SetValue(world);


                //at this point' we're ready to begin drawing
                //To start using any effect, you must call Effect.Begin
                //to start using the current technique (set in LoadGraphicsContent)
                effect.Begin(SaveStateMode.None);


                if (ShaderModel3)
                {
                    effect.CommitChanges();

                    //EffectPass.Begin will update the device to begin using the state information defined in the current pass
                    effect.CurrentTechnique.Passes["SinglePass"].Begin();

                    //sampleMesh contains all of the information required to draw the current mesh
                    graphicsDevice.DrawIndexedPrimitives(
                        PrimitiveType.TriangleList, meshPart.BaseVertex, 0,
                        meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);

                    //EffectPass.End must be called when the effect is no longer needed
                    effect.CurrentTechnique.Passes["SinglePass"].End();
                }
                else
                {
                    //EffectPass.Begin will update the device to begin using the state information defined in the current pass
                    effect.CurrentTechnique.Passes["Ambient"].Begin();

                    //sampleMesh contains all of the information required to draw the current mesh
                    graphicsDevice.DrawIndexedPrimitives(
                        PrimitiveType.TriangleList, meshPart.BaseVertex, 0,
                        meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);

                    //EffectPass.End must be called when the effect is no longer needed
                    effect.CurrentTechnique.Passes["Ambient"].End();


                    effect.CurrentTechnique.Passes["PointLight"].Begin();
                    foreach (PointLight pointLight in pointLights)
                    {
                        pointLight.Invalidate();
                        pointLight.UpdateLight(0);

                        effect.CommitChanges();

                        graphicsDevice.DrawIndexedPrimitives(
                            PrimitiveType.TriangleList, meshPart.BaseVertex, 0,
                            meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);
                    }
                    effect.CurrentTechnique.Passes["PointLight"].End();


                    if (!Settings.DisableBeamLighting)
                    {
                        effect.CurrentTechnique.Passes["BeamLight"].Begin();
                        foreach (BeamLight beamLight in beamLights)
                        {
                            beamLight.Invalidate();
                            beamLight.UpdateLight(0);

                            effect.CommitChanges();

                            graphicsDevice.DrawIndexedPrimitives(
                                PrimitiveType.TriangleList, meshPart.BaseVertex, 0,
                                meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);
                        }
                        effect.CurrentTechnique.Passes["BeamLight"].End();
                    }
                }

                //Likewise, Effect.End will end the current technique
                effect.End();
            }
        }

        

    }
}
